home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Applications / Eudora 1.3.1 / source / message.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-16  |  37.8 KB  |  1,335 lines  |  [TEXT/MPS ]

  1. #define FILE_NUM 25
  2. /* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */
  3. #pragma load EUDORA_LOAD
  4. #pragma segment Message
  5. TEHandle GetMessText(MessType **messH);
  6. int AppendMessage(TOCType **fromTocH,int fromN,TOCType **toTocH,Boolean copy);
  7. int MessErr;
  8. int FindAndCopyHeader(MessType **origMH,MessType **newMH,UPtr fromHead,short toHead);
  9. int TextFindAndCopyHeader(UPtr body,long size,MessType **newMH,UPtr fromHead,short toHead);
  10. UPtr FindHeaderString(UPtr text,UPtr headerName,long *size);
  11. FixMessTE(MessType **messH,short whichTXE,short oldNl);
  12. void WeedHeaders(UHandle buffer,long *weeded,short toWeed);
  13. MyWindowPtr OpenMessage(TOCType **tocH,int sumNum,MyWindowPtr win, Boolean showIt);
  14. void RemoveSelf(MessHandle messH,short head);
  15. #define HeaderName(num) (GetRString(scratch,HEADER_STRN+num),TrimWhite(scratch),scratch)
  16. void FindFrom(UPtr who, MessHandle messH);
  17. void Attribute(short attrId,MessHandle origMessH,MessHandle newMessH,Boolean atEnd);
  18. void XferCustomTable(MessHandle origMessH,MessHandle newMessH);
  19. /************************************************************************
  20.  * GetAMessage - grab a message
  21.  ************************************************************************/
  22. MyWindowPtr GetAMessage(TOCType **tocH,int sumNum,MyWindowPtr win, Boolean showIt)
  23. {
  24.     MessHandle messH;
  25. #ifdef DEBUG
  26.     if (BUG6) DebugStr("\p;hc;g");
  27. #endif
  28.     if (!tocH || (*tocH)->count<=sumNum) return(nil);
  29.     if (messH = (*tocH)->sums[sumNum].messH)
  30.     {
  31.         if (showIt)
  32.         {
  33.             if (!(*messH)->win->qWindow.visible)
  34.                 ShowMyWindow((*messH)->win);
  35.             SelectWindow((*messH)->win);
  36.         }
  37.         UsingWindow((*messH)->win);
  38.         return((*messH)->win);
  39.     }
  40.     else if ((*tocH)->which==OUT)
  41.         return(OpenComp(tocH,sumNum,win,showIt));
  42.     else
  43.         return(OpenMessage(tocH,sumNum,win,showIt));
  44. }
  45.  
  46. /**********************************************************************
  47.  * OpenMessage - open a message in its own window
  48.  **********************************************************************/
  49. MyWindowPtr OpenMessage(TOCType **tocH,int sumNum,MyWindowPtr win, Boolean showIt)
  50. {
  51.     Str255 title;
  52.     MessType **messH;
  53.     Boolean turvy = showIt && (CurrentModifiers()&optionKey)!=0;
  54.     TEHandle teh;
  55.  
  56.     CycleBalls();
  57.     if ((*tocH)->sums[sumNum].length > 32765)
  58.     {
  59.         WarnUser(TE_TOO_MUCH,(*tocH)->sums[sumNum].length);
  60.         return(nil);
  61.     }
  62.     
  63.     if ((messH = (MessType **)NewZHandle(sizeof(MessType)))==nil)
  64.         return(nil);
  65.         
  66.     win = GetNewMyWindow(MESSAGE_WIND,win,InFront,False,False);
  67.     if (!win)
  68.     {
  69.         DisposHandle(messH);
  70.         return(nil);
  71.     }
  72.     ((WindowPeek)win)->windowKind = MESS_WIN;
  73.  
  74.     (*tocH)->sums[sumNum].messH = messH;
  75.     (*messH)->win = win;
  76.     (*messH)->sumNum = sumNum;
  77.     (*messH)->tocH = tocH;
  78.     /* apply FLAG_OUT ex post facto */
  79.     if ((*tocH)->sums[sumNum].state==SENT || (*tocH)->sums[sumNum].state==UNSENT)
  80.         (*tocH)->sums[sumNum].flags |= FLAG_OUT;
  81.     
  82.     ((WindowPeek)win)->refCon = (long)messH;
  83.     win->close = MessClose;
  84.     win->menu = MessMenu;
  85.     win->gonnaShow = MessGonnaShow;
  86.     win->position = MessagePosition;
  87.     win->cursor = MessCursor;
  88.     win->button = CompButton;    /* it will do */
  89.     win->app1 = MessApp1;
  90.     LL_Push(MessList,messH);
  91.  
  92.     if (turvy)
  93.         ChangeStrn(PREF_STRN,PREF_SHOW_ALL,PrefIsSet(PREF_SHOW_ALL)?"\pn":"\py");
  94.     teh = GetMessText(messH);
  95.     win->ste = NewSTE(win,&win->contR,False,True,False);
  96.     if (turvy)
  97.         ChangeStrn(PREF_STRN,PREF_SHOW_ALL,PrefIsSet(PREF_SHOW_ALL)?"\pn":"\py");
  98.     
  99.     if (!teh || !win->ste)
  100.     {
  101.         if (teh) TEDispose(teh);
  102.         CloseMyWindow(win);
  103.         return(nil);
  104.     }
  105.     else
  106.     {
  107.         STEInstallText((*teh)->hText,win->ste);
  108.         (*(*(STEHandle)win->ste)->te)->teLength = (*teh)->teLength;
  109.         LDRef(teh)->hText = NuHandle(0);
  110.         (*teh)->teLength = 0;
  111.         TEDispose(teh);
  112.         (*messH)->txes[BODY] = (*(STEHandle)win->ste)->te;
  113.         (*messH)->stes[0] = win->ste;
  114.         (*(*messH)->stes[0])->dontFrame = True;
  115.         (*(*messH)->stes[0])->growBox = True;
  116.     }
  117.     MakeMessTitle(title,tocH,sumNum);
  118.     SetWTitle(win,title);
  119.     if (showIt)
  120.         ShowMyWindow(win);
  121.     return(win);
  122. }
  123.  
  124. /**********************************************************************
  125.  * MakeMessTitle - make a reasonable message title from a summary
  126.  **********************************************************************/
  127. void MakeMessTitle(UPtr title,TOCType **tocH,int sumNum)
  128. {
  129.     Str63 from, date, mailbox, subject;
  130.     Str63 pattern;
  131.     
  132.     PCopy(from,(*tocH)->sums[sumNum].from);
  133.     if (*from >    (*tocH)->sums[sumNum].fromTrunc)
  134.     {
  135.         *from =    MAX(1,(*tocH)->sums[sumNum].fromTrunc);
  136.         from[*from] = '…';
  137.     }
  138.     
  139.     PCopy(date,(*tocH)->sums[sumNum].date);
  140.     if (*date >    (*tocH)->sums[sumNum].dateTrunc)
  141.     {
  142.         *date =    MAX(1,(*tocH)->sums[sumNum].dateTrunc);
  143.         date[*date] = '…';
  144.     }
  145.     
  146.     PCopy(mailbox,(*tocH)->name);
  147.     PCopy(subject,(*tocH)->sums[sumNum].subj);
  148.     
  149.     utl_PlugParams(GetRString(pattern,MESS_TITLE_PLUG),title,mailbox,from,date,subject);
  150. }
  151.         
  152. /**********************************************************************
  153.  * GetMessText - put the text of a message into a window's txe record
  154.  **********************************************************************/
  155. TEHandle GetMessText(MessType **messH)
  156. {
  157.     MyWindowPtr win = (*messH)->win;
  158.     TOCType **tocH = (*messH)->tocH;
  159.     int sumNum = (*messH)->sumNum;
  160.     UHandle buffer = nil;
  161.     TEHandle teh = nil;
  162.     Rect dRect;
  163.     long weeded;
  164.     short tableId;
  165.     Handle table;
  166.     
  167.     /*
  168.      * allocate space for the text
  169.      */
  170.     if ((buffer=NuHandle((*tocH)->sums[sumNum].length))==nil)
  171.     {
  172.         WarnUser(NO_MESS_BUF,MemError());
  173.         return(nil);
  174.     }
  175.     
  176.     /*
  177.      * read it
  178.      */
  179.     LDRef(buffer);
  180.     if (MessErr=ReadMessage(tocH,sumNum,*buffer))
  181.         goto failure;
  182.     UL(buffer);
  183.     
  184.     /*
  185.      * now, set up the TERec
  186.      */
  187.     dRect = win->contR;
  188.     InsetRect(&dRect,4,0);
  189.     if ((teh=TENew(&dRect,&win->contR))==nil)
  190.     {
  191.         WarnUser(MESS_TE,MemError());
  192.         goto failure;
  193.     }
  194.     
  195.     /*
  196.      * weed headers?
  197.      */
  198.     if (!PrefIsSet(PREF_SHOW_ALL))
  199.     {
  200.         WeedHeaders(buffer,&weeded,BADHEAD_STRN);
  201.         (*messH)->weeded = weeded;
  202.     }
  203.     else
  204.         (*messH)->weeded = 0;
  205.     WeedHeaders(buffer,&weeded,FROM_STRN);
  206.     (*messH)->weeded += weeded;
  207.     
  208.     /*
  209.      * translate?
  210.      */
  211.     tableId = (*tocH)->sums[sumNum].tableId;
  212.     if (tableId==DEFAULT_TABLE) tableId = GetRLong(PREF_STRN+PREF_IN_XLATE);
  213.     if (tableId!=NO_TABLE && (table=GetResource('taBL',tableId)))
  214.     {
  215.         Byte *p,*end,*t;
  216.         long size = GetHandleSize(buffer);
  217.         end = *buffer+size;
  218.         t = *table;
  219.         for (p=*buffer;p<end;p++) *p = t[*p];
  220.     }
  221.     
  222.     /*
  223.      * put in the text...
  224.      */
  225.     DisposHandle((*teh)->hText);
  226.     (*teh)->hText = buffer;
  227.     (*teh)->teLength = GetHandleSize(buffer);
  228.  
  229.  
  230.     win->ro = True;
  231.     return(teh);
  232.     
  233. failure:
  234.     if (buffer) DisposHandle(buffer);
  235.     if (teh) TEDispose(teh);
  236.     return(nil);
  237. }
  238.  
  239. /**********************************************************************
  240.  * ReadMessage - read a given message into a preallocated buffer
  241.  **********************************************************************/
  242. int ReadMessage(TOCType **tocH,int sumNum,UPtr buffer)
  243. {
  244.     long count;
  245.     Str255 name;
  246.     
  247.     BlockMove((*tocH)->name,name,(*(*tocH)->name)+1);
  248.     count = (*tocH)->sums[sumNum].length;
  249.  
  250.     if (!(MessErr=BoxFOpen(tocH)))
  251.       if ((MessErr=SetFPos((*tocH)->refN,fsFromStart,
  252.                                                (*tocH)->sums[sumNum].offset)) ||
  253.               (MessErr=FSRead((*tocH)->refN,&count,buffer)))
  254.             FileSystemError(READ_MBOX,name,MessErr);
  255.  
  256.     return(MessErr);
  257. }
  258.  
  259. /**********************************************************************
  260.  * MoveMessage - transfer a message from one box to another
  261.  * called when the transfer menu is invoked with a message frontmost
  262.  **********************************************************************/
  263. int MoveMessage(TOCType **tocH,int sumNum,long dirId,UPtr toWhich,Boolean copy)
  264. {
  265.     TOCType **toTocH;
  266.  
  267.     CycleBalls();
  268.  
  269.     if ((toTocH = TOCByName(dirId,toWhich))==nil)
  270.         return(1);
  271.         
  272.     CycleBalls();
  273.     MessErr=AppendMessage(tocH,sumNum,toTocH,copy);
  274.     if (MessErr) return(MessErr);
  275.     (void) BoxFClose(tocH);
  276.     (void) BoxFClose(toTocH);
  277.  
  278.     CycleBalls();
  279.     if (!copy) DeleteSum(tocH,sumNum);
  280.         
  281.     CheckBox(FrontWindow());
  282.     return(MessErr);
  283. }
  284.  
  285. /**********************************************************************
  286.  * AppendMessage - add a message to a mailbox.    Message comes from another
  287.  * mailbox.  Things in here are a little touchy, as there are several
  288.  * things to do, any one of which could fail.  In order, this is
  289.  * what is done:
  290.  * 1. Move the bytes from one mailbox to the other.
  291.  * 2. Copy the message summary from one toc to the other, updating
  292.  *        tocH and sumNum in the message handle (if any).
  293.  * 3. If the message window is open, fix pointers so the message
  294.  *        belongs to the new box, not the old one.
  295.  * Steps 1 and 2 could fail.    In either case, no real harm is done,
  296.  * except that we might waste some space in the new mailbox.
  297.  * Step 3 shouldn't ever fail.
  298.  **********************************************************************/
  299. int AppendMessage(TOCType **fromTocH,int fromN,TOCType **toTocH,Boolean copy)
  300. {
  301.     UHandle buffer = nil;
  302.     MSumType sum;
  303.     long eof;
  304.     short err=0;
  305.     MessHandle fromMH;
  306.     
  307.     /*
  308.      * open the relevant mailboxes
  309.      */
  310.     MessErr = BoxFOpen(fromTocH);
  311.     if (MessErr) return(MessErr);
  312.     MessErr = BoxFOpen(toTocH);
  313.     if (MessErr) return(MessErr);
  314.     
  315.     /*
  316.      * if it's an outgoing message, save it first
  317.      */
  318.     fromMH = (*fromTocH)->sums[fromN].messH;
  319.     if (fromMH)
  320.         if ((*fromTocH)->which==OUT)
  321.         {
  322.             MyWindowPtr win = (*fromMH)->win;
  323.             if ((win->isDirty||(*fromTocH)->sums[fromN].flags&FLAG_NBK) &&
  324.                     !SaveComp(win)) return(1);
  325.         }
  326.         else if (!(*fromMH)->win->ro) MessSwapTXE(fromMH);
  327.         
  328.     /*
  329.      * copy the message from one to the other
  330.      */
  331.     eof = FindTOCSpot(toTocH,(*fromTocH)->sums[fromN].length);
  332.     MessErr = CopyFBytes((*fromTocH)->refN,(*fromTocH)->sums[fromN].offset,
  333.         (*fromTocH)->sums[fromN].length,(*toTocH)->refN,eof);
  334.     if (MessErr)
  335.     {
  336.         FileSystemError(COPY_FAILED,(*toTocH)->name,MessErr);
  337.         return(MessErr);
  338.     }
  339.     (void) SetEOF((*toTocH)->refN,eof+(*fromTocH)->sums[fromN].length);
  340.     
  341.     /*
  342.      * now, create a new summary for the copied message, and put it in the
  343.      * new TOC.
  344.      */
  345.     sum = (*fromTocH)->sums[fromN];
  346.     sum.offset = eof;
  347.     sum.selected = False;
  348.     sum.messH = nil;            /* break connection with open message window */
  349.     if (!sum.seconds) sum.seconds = GMTDateTime();
  350.     if ((*fromTocH)->which==OUT)
  351.     {
  352.         if (sum.state!=SENT) sum.state=UNSENT;
  353.         sum.tableId = NO_TABLE;        /* don't translate */
  354.     }
  355.         
  356.     if ((*toTocH)->count)
  357.     {
  358.         MessErr = PtrAndHand(&sum,toTocH,sizeof(sum));
  359.         if (MessErr) return(MessErr);
  360.     }
  361.     else
  362.         (*toTocH)->sums[0] = sum;
  363.     (*toTocH)->dirty = True;
  364.     (*toTocH)->needRedo = MIN((*toTocH)->needRedo,(*toTocH)->count);
  365.     (*toTocH)->count++;
  366.     
  367.     /*
  368.      * the message window, if any, should be closed
  369.      */
  370.     if (!copy && (*fromTocH)->sums[fromN].messH)
  371.         CloseMyWindow((*(MessHandle)(*fromTocH)->sums[fromN].messH)->win);
  372.  
  373.     return(noErr);
  374. }
  375.  
  376.  
  377. /**********************************************************************
  378.  * MoveSelectedMessages - transfer all selected messages from one mail
  379.  * box to another.
  380.  **********************************************************************/
  381. int MoveSelectedMessages(TOCType **tocH,long dirId,UPtr toWhich,Boolean copy)
  382. {
  383.     TOCType **toTocH;
  384.     int sumNum;
  385.     int lastSelected = -1;
  386.     Str31 trashName;
  387.     short oldCount;
  388.     Boolean toTrash = dirId==MyDirId && EqualString(toWhich,GetRString(trashName,TRASH),False,True);
  389.     
  390.     if ((toTocH = TOCByName(dirId,toWhich))==nil)
  391.         return(1);
  392.     
  393.     if (!PrefIsSet(PREF_EASY_DELETE) && (toTrash || (*tocH)->which==OUT))
  394.     {
  395.         for (sumNum = 0; sumNum < (*tocH)->count; sumNum++)
  396.             if ((*tocH)->sums[sumNum].selected &&
  397.                 ((*tocH)->sums[sumNum].state==UNREAD ||
  398.                 (*tocH)->sums[sumNum].state==QUEUED)) break;
  399.         if (sumNum<(*tocH)->count)
  400.         {
  401.             Str31 sent,delete;
  402.             GetRString(sent,(*tocH)->sums[sumNum].state==QUEUED ? SENT_VERB:READ_VERB);
  403.             GetRString(delete,toTrash ? DELETE_VERB : TRANSFER_VERB);
  404.             MyParamText(sent,delete,"","");
  405.             if (ReallyDoAnAlert(TRASH_UNREAD_ALRT,Caution)!=1)
  406.                 return(0);
  407.         }
  408.     }
  409.     
  410.     for (sumNum = 0; sumNum < (*tocH)->count; sumNum++)
  411.     {
  412.         if ((*tocH)->sums[sumNum].selected)
  413.         {
  414.             CycleBalls();
  415.             oldCount = (*tocH)->count;
  416.             MessErr=AppendMessage(tocH,sumNum,toTocH,copy);
  417.             if (MessErr) break;
  418.             if (oldCount!=(*tocH)->count)
  419.             {
  420.                 lastSelected = sumNum;
  421.                 sumNum--;
  422.             }
  423.             else if (!copy)
  424.             {
  425.                 DeleteSum(tocH,sumNum);
  426.                 lastSelected = sumNum;
  427.                 sumNum--;         /* back up, so we can try again */
  428.             }
  429.         }
  430.     }
  431.     (void) BoxFClose(tocH);
  432.     (void) BoxFClose(toTocH);
  433.  
  434.     if ((*tocH)->win && !copy) BoxSelectAfter((*tocH)->win,lastSelected);
  435.     CheckBox(FrontWindow());
  436.         
  437.     return(MessErr);
  438. }
  439.  
  440. /**********************************************************************
  441.  * DeleteMessage - delete a summary from a toc, and fix the screen, too
  442.  **********************************************************************/
  443. void DeleteMessage(TOCType **tocH, int sumNum)
  444. {
  445.     MessType **messH = (MessType **)(*tocH)->sums[sumNum].messH;
  446.     Boolean dirt = 0;
  447.     Str63 trashName;
  448.     int oldN = (*tocH)->count;
  449.     
  450.     if ((*tocH)->which!=TRASH)
  451.     {
  452.         GetRString(trashName,TRASH);
  453.         MoveMessage(tocH,sumNum,MyDirId,trashName,False);
  454.     }
  455.     else
  456.     {
  457.         if (messH) CloseMyWindow((*messH)->win);
  458.         DeleteSum(tocH,sumNum);
  459.     }
  460.  
  461. /************************************************************************
  462.  * MessageError - return the most recent error code from these functions
  463.  ************************************************************************/
  464. int MessageError(void)
  465. {
  466.     return(MessErr);
  467. }
  468.  
  469. /************************************************************************
  470.  * WeedHeaders - weed a message's headers, leaving only the interesting
  471.  * ones.
  472.  ************************************************************************/
  473. void WeedHeaders(UHandle buffer,long *weeded,short toWeed)
  474. {
  475.     UPtr spot = LDRef(buffer);
  476.     UPtr done = spot;
  477.     UPtr end = spot + GetHandleSize(buffer);
  478.     long size;
  479.     Str31 badName;
  480.     short bad;
  481.     
  482.     while (spot<end)
  483.     {
  484.         if (*spot=='\n') break;
  485.         for (bad=1;*GetRString(badName,toWeed+bad);bad++)
  486.             if (!striscmp(spot,badName+1))
  487.             {
  488.                 while (spot<end)
  489.                     if (*spot++=='\n')
  490.                         if (*spot!=' ' && *spot!='\t') break;
  491.                 goto nextHead;
  492.             }
  493.         while (spot<end)
  494.             if ((*done++ = *spot++)=='\n')
  495.                 if (*spot!=' ' && *spot!='\t') break;
  496.         nextHead:;
  497.     }
  498.     
  499.     while (spot<end) *done++ = *spot++;
  500.     size = done - *buffer;
  501.     if (weeded) *weeded = end-done;
  502.     HUnlock(buffer);
  503.     SetHandleBig(buffer,size);
  504. }
  505.  
  506. /************************************************************************
  507.  * SetMessText - stick some text into one of the fields of a message.
  508.  ************************************************************************/
  509. void SetMessText(MessType **messH,short whichTXE,UPtr string,short size)
  510. {
  511.     TEHandle teh = (*messH)->txes[whichTXE];
  512.     int oldNl = CountTeLines(teh);
  513.  
  514.     TESetText(string,size,teh);
  515.     FixMessTE(messH,whichTXE,oldNl);
  516. }
  517.  
  518. /************************************************************************
  519.  * AppendMessText - stick some text after one of the fields of a message.
  520.  ************************************************************************/
  521. void AppendMessText(MessType **messH,short whichTXE,UPtr string,short size)
  522. {
  523.     TEHandle teh = (*messH)->txes[whichTXE];
  524.     int oldNl = CountTeLines(teh);
  525.  
  526.     NoScrollTESetSelect(INFINITY,INFINITY,teh);
  527.     TEInsert(string,size,teh);
  528.     FixMessTE(messH,whichTXE,oldNl);
  529. }
  530.  
  531. /************************************************************************
  532.  * FixMessTE - fix up a te in a message
  533.  ************************************************************************/
  534. FixMessTE(MessType **messH,short whichTXE,short oldNl)
  535. {
  536.     MyWindowPtr win = (*messH)->win;
  537.     TEHandle oldTXE;
  538.     short newNl;
  539.     
  540.     if (win)
  541.     {
  542.         oldTXE = win->txe;
  543.         win->txe = (*messH)->txes[whichTXE];
  544.         newNl = CountTeLines(win->txe);
  545.         CompTxChanged(win,oldNl,newNl,False);
  546.         INVAL_RECT(&(*win->txe)->destRect);
  547.         win->txe = oldTXE;
  548.     }
  549. }
  550.  
  551.  
  552. /************************************************************************
  553.  * DoIterativeThingy - do something over all selected messages
  554.  ************************************************************************/
  555. void DoIterativeThingy(TOCType **tocH,int item,long modifiers,short toWhom)
  556. {
  557.     int sumNum;
  558.     MessType **messH;
  559.     MyWindowPtr win;
  560.     short lastSelected = -1;
  561.     Str255 title;
  562.  
  563. #ifdef PERF
  564.     PerfControl(ThePGlobals,True);
  565. #endif PERF
  566.  
  567.     if (item==MESSAGE_DELETE_ITEM && (*tocH)->which!=TRASH)
  568.     {
  569.         GetRString(title,TRASH);
  570.         MoveSelectedMessages(tocH,MyDirId,title,False);
  571.         return;
  572.     }
  573.     
  574.     OpenProgress();
  575.     
  576.     for (sumNum=(*tocH)->count-1;!EjectBuckaroo && sumNum>=0;sumNum--)
  577.     {
  578.         if ((*tocH)->sums[sumNum].selected)
  579.         {
  580.             lastSelected = sumNum;
  581.             MakeMessTitle(title,tocH,sumNum);
  582.             Progress(NoChange,title);
  583.             switch(item)
  584.             {
  585.                 case MESSAGE_DELETE_ITEM:
  586.                     DeleteMessage(tocH,sumNum);
  587.                     break;
  588.                 default:
  589.                     messH = (*tocH)->sums[sumNum].messH;
  590.                     if (messH || GetAMessage(tocH,sumNum,nil,False))
  591.                     {
  592.                         win = (*(MessType **)(*tocH)->sums[sumNum].messH)->win;
  593.                         UsingWindow(win);
  594.                         switch(item)
  595.                         {
  596.                             case MESSAGE_SALVAGE_ITEM:
  597.                                 DoSalvageMessage(win);
  598.                                 break;
  599.                             case MESSAGE_FORWARD_ITEM:
  600.                                 DoForwardMessage(win,toWhom);
  601.                                 break;
  602.                             case MESSAGE_REPLY_ITEM:
  603.                                 DoReplyMessage(win,modifiers,toWhom,True);
  604.                                 break;
  605.                             case MESSAGE_REDISTRIBUTE_ITEM:
  606.                                 DoRedistributeMessage(win,toWhom);
  607.                                 break;
  608.                         }
  609.                         if (!messH)
  610.                             CloseMyWindow(win);
  611.                         else
  612.                             NotUsingWindow(win);
  613.                     }
  614.                 }
  615.             }
  616.             MonitorGrow();
  617.     }
  618.     CloseProgress();
  619.     BoxSelectAfter((*tocH)->win,lastSelected);
  620.     CheckBox(FrontWindow());
  621. #ifdef PERF
  622.     PerfControl(ThePGlobals,False);
  623. #endif
  624. }
  625.  
  626. /************************************************************************
  627.  * BoxNextSelected - find first selection in a mailbox
  628.  ************************************************************************/
  629. short BoxNextSelected(TOCHandle tocH,short afterNum)
  630. {
  631.     int sNum, count;
  632.     
  633.     count = (*tocH)->count;
  634.     
  635.     for (sNum=afterNum+1;sNum<count;sNum++)
  636.         if ((*tocH)->sums[sNum].selected) return(sNum);
  637.     
  638.     return(-1);
  639. }
  640.  
  641. #pragma segment MsgOps
  642. /************************************************************************
  643.  * DoSalvageMessage - glean what you can from a bounced message's headers
  644.  ************************************************************************/
  645. short DoSalvageMessage(MyWindowPtr win)
  646. {
  647.     MessType **origMessH = (MessType **)((WindowPeek)win)->refCon;
  648.     MessType **newMessH;
  649.     MyWindowPtr newWin;
  650.     Str255 scratch;
  651.     
  652.     if (newWin=DoComposeNew(0))
  653.     {
  654.         newMessH = (MessType **)newWin->qWindow.refCon;
  655.         XferCustomTable(origMessH,newMessH);        
  656.         if (win->qWindow.windowKind==COMP_WIN)
  657.         {
  658.             short tx;
  659.             for (tx=0;tx<sizeof((*newMessH)->txes)/sizeof(TEHandle);tx++)
  660.             {
  661.                 TESetText(LDRef((*(*origMessH)->txes[tx])->hText),
  662.                     (*(*origMessH)->txes[tx])->teLength,(*newMessH)->txes[tx]);
  663.                 UL((*(*origMessH)->txes[tx])->hText);
  664.             }
  665.             (*(*newMessH)->tocH)->dirty = True;
  666.             SumOf(newMessH)->flags = SumOf(origMessH)->flags;
  667.         }
  668.         else
  669.         {
  670.             UPtr spot, oldSpot, beginning;
  671.             long size, total;
  672.             Boolean toFound;
  673.             Str63 received;
  674.             
  675.             beginning = spot = LDRef((*Win2Body(win))->hText);
  676.             total = size = GetHandleSize((*Win2Body(win))->hText);
  677.             
  678.             /*
  679.              * find the last "Received:" header
  680.              */
  681.             GetRString(received,RECEIVED_HEAD); TrimWhite(received);
  682.             for (oldSpot = nil;
  683.                      spot=FindHeaderString(spot,received,&size);
  684.                      oldSpot=spot,spot+=size,size=total-(spot-beginning-size));
  685.             
  686.             /*
  687.              * copy the relevant parts
  688.              */
  689.             if (!oldSpot) oldSpot=beginning;
  690.             {
  691.                 if (oldSpot!=beginning)
  692.                     SumOf(newMessH)->flags &= ~FLAG_SIG;
  693.                 size = total - (oldSpot-beginning);
  694.                 TextFindAndCopyHeader(oldSpot,size,newMessH,HeaderName(SUBJ_HEAD),SUBJ_HEAD);
  695.                 TextFindAndCopyHeader(oldSpot,size,newMessH,HeaderName(CC_HEAD),CC_HEAD);
  696.                 TextFindAndCopyHeader(oldSpot,size,newMessH,HeaderName(ATTACH_HEAD),ATTACH_HEAD);
  697.                 toFound = TextFindAndCopyHeader(oldSpot,size,newMessH,HeaderName(TO_HEAD),TO_HEAD);
  698.                 TextFindAndCopyHeader(oldSpot,size,newMessH,HeaderName(BCC_HEAD),BCC_HEAD);
  699.                 /*
  700.                  * find the body
  701.                  */
  702.                 for (spot=oldSpot;spot<oldSpot+size-1;spot++)
  703.                     if (spot[0]=='\n' && spot[1]=='\n') break;
  704.                 spot += 2;
  705.                 if (spot<oldSpot+size-1)
  706.                     SetMessText(newMessH,HEAD_LIMIT-1,spot,total-(spot-beginning));
  707.                 HUnlock((*Win2Body(win))->hText);
  708.             }
  709.         }
  710.         UpdateSum(newMessH,SumOf(newMessH)->offset,SumOf(newMessH)->length);
  711.         ShowMyWindow(newWin);
  712.         newWin->isDirty = False;
  713.     }
  714.     return(!newWin);
  715. }
  716.  
  717. /************************************************************************
  718.  * RemoveSelf - Remove "me" from a list of addresses
  719.  *    Ray Davison, SFU
  720.  ************************************************************************/
  721. void RemoveSelf(MessHandle messH,short head)
  722. {
  723.                 Str63 temp,dummy;
  724.                 UHandle rawMyself, cookedMyself;
  725.                 UHandle rawAddress, spewHandle;
  726.                 UHandle myself=NewHandle(0);
  727.                 long offset, meOffset;
  728.                 Boolean removed = False;
  729.                 Handle text = (*(*messH)->txes[head-1])->hText;
  730.                 short nLines = CountTeLines((*messH)->txes[head-1]);
  731.                 
  732.                 /* Get a definition of who I am */
  733.                 
  734.                 GetRString(temp, ME);
  735.                 PtrAndHand(temp+1, myself, temp[0]);
  736.                 PtrAndHand(",", myself, 1);
  737.                 GetPOPInfo(temp, dummy);
  738.                 PtrAndHand(temp+1, myself, temp[0]);
  739.                 PtrAndHand(",", myself, 1);
  740.                 GetReturnAddr(temp ,True);
  741.                 PtrAndHand(temp+1, myself, temp[0]);
  742.                 rawMyself = SuckAddresses(myself, GetHandleSize(myself), False);
  743.                 cookedMyself = ExpandAliases(rawMyself, 0, False);
  744.                 DisposHandle(rawMyself);
  745.                 DisposHandle(myself);
  746.                 
  747.                 /* expand the text */
  748.                 
  749.                 rawAddress = SuckAddresses(text, GetHandleSize(text), True);
  750.                 SetHandleSize(text, 0);
  751.                 
  752.                 /* Remove myself from address */
  753.                 LDRef(cookedMyself);
  754.                 for (offset=0; (*rawAddress)[offset]; offset += (*rawAddress)[offset]+2)
  755.                 {
  756.                                 /* clean up the address */
  757.                                 LDRef(rawAddress);
  758.                                 spewHandle = SuckPtrAddresses((*rawAddress)+offset+1,
  759.                                                 (*rawAddress)[offset], False);
  760.                                 UL(rawAddress);
  761.                                 LDRef(spewHandle);
  762.                                 /* look for this in the "me" addresses */
  763.                                 for (meOffset=0; (*cookedMyself)[meOffset]; meOffset += (*cookedMyself)[meOffset]+2)
  764.                                 {
  765.                                 if (EqualString(*spewHandle,*cookedMyself+meOffset,False,True))
  766.                                     break;
  767.                                 }
  768.                                 DisposHandle(spewHandle);
  769.                 
  770.                                 /* if we didn't find it, then add this address to the result */
  771.                                 if (!(*cookedMyself)[meOffset])
  772.                                 {
  773.                                                 LDRef(rawAddress);
  774.                                                 if (GetHandleSize(text)) PtrAndHand(", ", text, 2);
  775.                                                 PtrAndHand((*rawAddress)+offset+1,text,(*rawAddress)[offset]);
  776.                                                 HUnlock(rawAddress);
  777.                                 }
  778.                                 else removed = True;
  779.                 }
  780.                 
  781.                 DisposHandle(cookedMyself);
  782.                 DisposHandle(rawAddress);
  783.                 if (removed)
  784.                 {
  785.                     (*(*messH)->txes[head-1])->teLength = GetHandleSize(text);
  786.                     FixMessTE(messH,head-1,nLines);
  787.                 }
  788. }
  789.  
  790. /************************************************************************
  791.  * ReopenMessage - reopen the current message
  792.  ************************************************************************/
  793. MyWindowPtr ReopenMessage(MyWindowPtr win)
  794. {
  795.     TOCHandle tocH = (*Win2MessH(win))->tocH;
  796.     short sumNum = (*Win2MessH(win))->sumNum;
  797.     
  798.     CloseMyWindow(win);
  799.     return(OpenMessage(tocH,sumNum,nil,True));
  800. }
  801.  
  802. /************************************************************************
  803.  * FindFrom - find a (nicely formatted) From address
  804.  ************************************************************************/
  805. void FindFrom(UPtr who, MessHandle messH)
  806. {
  807.     UPtr found;
  808.     Str31 header;
  809.   long len;
  810.     
  811.     if ((*(*messH)->tocH)->which==OUT)
  812.     {
  813.       len = (*(*messH)->txes[FROM_HEAD-1])->teLength;
  814.         *who = MIN(63,len);
  815.         BlockMove(*(*(*messH)->txes[FROM_HEAD-1])->hText,who+1,*who);
  816.         if (*who) BeautifyFrom(who);
  817.     }
  818.     else
  819.     {
  820.          len = (*BodyOf(messH))->teLength;
  821.         GetRString(header,FROM_HEAD+HEADER_STRN);
  822.         if (found = FindHeaderString(LDRef((*BodyOf(messH))->hText),header,&len))
  823.         {
  824.             *who = MIN(len,254);
  825.             BlockMove(found,who+1,*who);
  826.             who[*who+1] = 0;
  827.             BeautifyFrom(who);
  828.         }
  829.         else *who = 0;
  830.         UL((*BodyOf(messH))->hText);
  831.     }
  832. }
  833.  
  834. /************************************************************************
  835.  * QuoteLines - put a quote prefix before the specified TextEdit lines
  836.  ************************************************************************/
  837. void QuoteLines(MessType **messH,short whichTXE,int from,int to,short pfid)
  838. {
  839.     GrafPtr oldPort;
  840.     int oldNl;
  841.     MyWindowPtr win=(*messH)->win;
  842.     TEHandle teh = (*messH)->txes[whichTXE];
  843.     long ticks = TickCount();
  844.     short lastSpot, spot;
  845.     short this;
  846.     short expand=0;
  847.     short bSize;
  848.     UPtr copied;
  849.     Str15 prefix;
  850.     
  851.     GetPort(&oldPort);SetPort((*teh)->inPort);
  852.     
  853.     GetRString(prefix,pfid);
  854.     if (!*prefix) return;
  855.     oldNl = CountTeLines(teh);
  856. #define HARD_NL(i) (!(*teh)->lineStarts[i] ||\
  857.                 (*(*teh)->hText)[(*teh)->lineStarts[i]-1] == '\n')
  858.  
  859.     for (this=to;this>=from;this--)
  860.         if (HARD_NL(this)) expand += *prefix;
  861.     if (expand)
  862.     {
  863.         SetHandleBig((*teh)->hText,(*teh)->teLength+expand);
  864.         if (MemError()) return;
  865.         lastSpot = (*teh)->teLength;
  866.         copied = *(*teh)->hText+lastSpot+expand;
  867.         for (this=to;this>=from;this--)
  868.             if (HARD_NL(this))
  869.             {
  870.                 spot = (*teh)->lineStarts[this];
  871.                 bSize = lastSpot-spot;
  872.                 BlockMove(*(*teh)->hText+spot,copied-bSize,bSize);
  873.                 copied -= bSize + *prefix;
  874.                 BlockMove(prefix+1,copied,*prefix);
  875.                 lastSpot = spot;
  876.             }
  877.         (*teh)->teLength += expand;
  878.         TECalText(teh);
  879.         INVAL_RECT(&(*teh)->viewRect);
  880.         UpdateMyWindow(win);
  881.     }
  882.     win->txe = teh;
  883.     CompTxChanged((*teh)->inPort,oldNl,CountTeLines(teh),True);
  884.     SetPort(oldPort);
  885. }
  886.  
  887. /************************************************************************
  888.  * PrependMessText - stick some text before one of the fields of a message.
  889.  ************************************************************************/
  890. void PrependMessText(MessType **messH,short whichTXE,UPtr string,short size)
  891. {
  892.     TEHandle teh = (*messH)->txes[whichTXE];
  893.     int oldNl = CountTeLines(teh);
  894.  
  895.     NoScrollTESetSelect(0,0,teh);
  896.     TEInsert(string,size,teh);
  897.     FixMessTE(messH,whichTXE,oldNl);
  898. }
  899.  
  900. /************************************************************************
  901.  * FindHeaderString - pick the given header out of a message, by name
  902.  ************************************************************************/
  903. UPtr FindHeaderString(UPtr text,UPtr headerName,long *size)
  904. {
  905.     UPtr spot,end;
  906.     char header[MAX_HEADER];
  907.     
  908.     for (end = text+*size; text<end; text = spot+1)
  909.     {
  910.         for (spot=text;spot<end;spot++) if (*spot == '\n') break;
  911.         BlockMove(text,header+1,*headerName);
  912.         *header = *headerName;
  913.         if (EqualString(header,headerName,False,False))
  914.         {
  915.             text += *headerName;
  916.             while (*text==' ' || *text=='\t') text++;
  917.             for (spot--; IsWhite(*spot); spot--);
  918.             *size = spot-text+1;
  919.             return(spot>=text ? text : nil);
  920.         }
  921.     }
  922.     return(nil);
  923. }
  924. /************************************************************************
  925.  * DoReplyMessage - craft a reply to a message
  926.  ************************************************************************/
  927. MyWindowPtr DoReplyMessage(MyWindowPtr win, long modifiers, short toWhom,Boolean vis)
  928. {
  929.     MessHandle origMessH = (MessType **)((WindowPeek)win)->refCon;
  930.     MessHandle newMessH;
  931.     Str255 subj,scratch;
  932.     short bodyOffset = -1;
  933.     MyWindowPtr newWin=nil;
  934.     short nLines, r;
  935.     TEHandle teh;
  936.     short len;
  937.     Boolean all = PrefIsSet(PREF_REPLY_ALL);
  938.     Boolean quote = !(modifiers & shiftKey);
  939.     Boolean notMe = PrefIsSet(PREF_NOT_ME);
  940.     
  941.     if (modifiers&optionKey) all = !all;
  942.     
  943.     if (newWin=DoComposeNew(toWhom))
  944.     {
  945.         newMessH = (MessHandle) newWin->qWindow.refCon;
  946.         
  947.         XferCustomTable(origMessH,newMessH);
  948.         
  949.         /* handle the subject */
  950.         FindAndCopyHeader(origMessH,newMessH,HeaderName(SUBJ_HEAD),SUBJ_HEAD);
  951.         GetRString(scratch,REPLY_INTRO);
  952.         BlockMove(*(*(*newMessH)->txes[SUBJ_HEAD-1])->hText,subj+1,*scratch);
  953.         *subj = *scratch;
  954.         if (!EqualString(subj,scratch,False,False))
  955.             PrependMessText(newMessH,SUBJ_HEAD-1,scratch+1,*scratch);
  956.             
  957.         /* reply to sender */
  958.         if (!toWhom)
  959.         {
  960.             for (r=1;*GetRString(scratch,REPLY_STRN+r);r++)
  961.             {
  962.                 TrimWhite(scratch);
  963.                 if (FindAndCopyHeader(origMessH,newMessH,scratch,TO_HEAD)) break;
  964.             }
  965.       }
  966.         
  967.         /* bring over the other recipients and cc's, if desired */
  968.         if (all && !toWhom)
  969.         {
  970.             FindAndCopyHeader(origMessH,newMessH,HeaderName(TO_HEAD),TO_HEAD);
  971.             FindAndCopyHeader(origMessH,newMessH,HeaderName(CC_HEAD),CC_HEAD);
  972.  
  973.             /* remove self, if desired */
  974.             if (notMe)
  975.             {
  976.                 RemoveSelf(newMessH,TO_HEAD);
  977.                 RemoveSelf(newMessH,CC_HEAD);
  978.             }
  979.         }
  980.         
  981.         if (quote)
  982.         {
  983.             bodyOffset = SumOf(origMessH)->bodyOffset-(*origMessH)->weeded;
  984.             while ((*(*Win2Body(win))->hText)[bodyOffset] == '\n' &&
  985.                          bodyOffset<(*Win2Body(win))->teLength) bodyOffset++;
  986.             len = (*Win2Body(win))->teLength-bodyOffset;
  987.         }
  988.         else if ((*Win2Body(win))->selStart != (*Win2Body(win))->selEnd)
  989.         {
  990.             bodyOffset = (*Win2Body(win))->selStart;
  991.             len = (*Win2Body(win))->selEnd - bodyOffset;
  992.         }
  993.         
  994.         if (bodyOffset >= 0)
  995.         {
  996.             while (len && (*(*Win2Body(win))->hText)[bodyOffset+len-1]=='\n') len--;
  997.             {
  998.                 UPtr mpw_bug = LDRef((*Win2Body(win))->hText);
  999.                 SetMessText(newMessH,HEAD_LIMIT-1,mpw_bug+bodyOffset,len);
  1000.             }
  1001.             HUnlock((*Win2Body(win))->hText);
  1002.             UpdateMyWindow(newWin);
  1003.             
  1004.             /*
  1005.              * make sure the last line is blank
  1006.              */
  1007.             teh = (*newMessH)->txes[HEAD_LIMIT-1];
  1008.             nLines = (*teh)->nLines-1;
  1009.             if (!nLines ||
  1010.                     (*teh)->lineStarts[nLines-1]-(*teh)->lineStarts[nLines-2]!=1)
  1011.                 AppendMessText(newMessH,HEAD_LIMIT-1,"\n",1);
  1012.  
  1013.             /*
  1014.              * quote them
  1015.              */
  1016.             QuoteLines(newMessH,HEAD_LIMIT-1,0,nLines,QUOTE_PREFIX);
  1017.             
  1018.             /*
  1019.              * annotate
  1020.              */
  1021.             Attribute(all ? ATTRIBUTION:REP_SEND_ATTR,origMessH,newMessH,False);
  1022.         }
  1023.  
  1024.         /*
  1025.          * copy priority (not my idea)
  1026.          */
  1027.         if (!PrefIsSet(PREF_NO_XF_PRIOR))
  1028.             SumOf(newMessH)->origPriority = SumOf(newMessH)->priority =
  1029.                 SumOf(origMessH)->origPriority;
  1030.         
  1031.         if (vis)
  1032.         {
  1033.             SetState((*origMessH)->tocH,(*origMessH)->sumNum,REPLIED);
  1034.             UpdateSum(newMessH,SumOf(newMessH)->offset,SumOf(newMessH)->length);
  1035.             ShowMyWindow(newWin);
  1036.         }
  1037.         newWin->isDirty = False;
  1038.     }
  1039.     return(newWin);
  1040. }
  1041.  
  1042. /************************************************************************
  1043.  * Attribute - make an attribution
  1044.  ************************************************************************/
  1045. void Attribute(short attrId,MessHandle origMessH,MessHandle newMessH,Boolean atEnd)
  1046. {
  1047.     Str63 template,date;
  1048.     Str127 who;
  1049.     Str255 attribution;
  1050.     
  1051.     GetRString(template,attrId);
  1052.     if (*template)
  1053.     {
  1054.         MSumPtr sum = LDRef((*origMessH)->tocH)->sums+(*origMessH)->sumNum;
  1055.         FindFrom(who,origMessH);
  1056.         if (*who)
  1057.         {
  1058.             PCopy(date,sum->date);
  1059.             if (date[1]==optSpace) date[1] = ' ';
  1060.             utl_PlugParams(template,attribution,who,date,sum->subj,nil);
  1061.             if (atEnd)
  1062.                 AppendMessText(newMessH,HEAD_LIMIT-1,attribution+1,*attribution);
  1063.             else
  1064.                 PrependMessText(newMessH,HEAD_LIMIT-1,attribution+1,*attribution);
  1065.         }
  1066.         UL((*origMessH)->tocH);
  1067.     }
  1068. }
  1069.  
  1070. /************************************************************************
  1071.  * DoRedistributeMessage - craft a reply to a message
  1072.  ************************************************************************/
  1073. short DoRedistributeMessage(MyWindowPtr win,short toWhom)
  1074. {
  1075.     MessType **origMessH = (MessType **)((WindowPeek)win)->refCon;
  1076.     MessType **newMessH;
  1077.     Str255 scratch, who;
  1078.     int bodyOffset;
  1079.     MyWindowPtr newWin;
  1080.     TEHandle body;
  1081.     
  1082.     if (newWin=DoComposeNew(toWhom))
  1083.     {
  1084.         newMessH = (MessType **)newWin->qWindow.refCon;
  1085.         XferCustomTable(origMessH,newMessH);        
  1086.         SetMessText(newMessH,FROM_HEAD-1,"",0);
  1087.         FindAndCopyHeader(origMessH,newMessH,HeaderName(SUBJ_HEAD),SUBJ_HEAD);
  1088.         FindAndCopyHeader(origMessH,newMessH,HeaderName(FROM_HEAD),FROM_HEAD);
  1089.         FindAndCopyHeader(origMessH,newMessH,HeaderName(ATTACH_HEAD),ATTACH_HEAD);
  1090.         GetReturnAddr(who,True);
  1091.         *scratch = (*(*newMessH)->txes[FROM_HEAD-1])->teLength;
  1092.         BlockMove(*(*(*newMessH)->txes[FROM_HEAD-1])->hText,scratch+1,*scratch);
  1093.         if (!EqualString(scratch,who,False,True))
  1094.         {
  1095.             ComposeRString(scratch,REDIST_ANNOTATE,who);
  1096.             AppendMessText(newMessH,FROM_HEAD-1,scratch+1,*scratch);
  1097.         }
  1098.         bodyOffset = (*(*origMessH)->tocH)->sums[(*origMessH)->sumNum].bodyOffset;
  1099.         bodyOffset -= (*origMessH)->weeded;
  1100.         body = Win2Body(win);
  1101.         while((*(*body)->hText)[bodyOffset]=='\n' && bodyOffset<(*body)->teLength-1)
  1102.             bodyOffset++;
  1103.         SetMessText(newMessH,HEAD_LIMIT-1,
  1104.                          LDRef((*Win2Body(win))->hText)+bodyOffset,
  1105.                          (*Win2Body(win))->teLength-bodyOffset);
  1106.         HUnlock((*Win2Body(win))->hText);
  1107.         if (SumOf(origMessH)->state!=SENT && SumOf(origMessH)->state!=UNSENT)
  1108.             SumOf(newMessH)->flags &= ~FLAG_SIG;
  1109.  
  1110.         /*
  1111.          * copy priority
  1112.          */
  1113.         SumOf(newMessH)->origPriority = SumOf(newMessH)->priority =
  1114.             SumOf(origMessH)->origPriority;
  1115.         
  1116.         SetState((*origMessH)->tocH,(*origMessH)->sumNum,REDIST);
  1117.         UpdateSum(newMessH,SumOf(newMessH)->offset,SumOf(newMessH)->length);
  1118.         ShowMyWindow(newWin);
  1119.         newWin->isDirty = False;
  1120.     }
  1121.     return(!newWin);
  1122. }
  1123.  
  1124. /************************************************************************
  1125.  * DoForwardMessage - forward a message to someone
  1126.  ************************************************************************/
  1127. short DoForwardMessage(MyWindowPtr win,short toWhom)
  1128. {
  1129.     MessType **origMessH = (MessType **)((WindowPeek)win)->refCon;
  1130.     MessType **newMessH;
  1131.     MyWindowPtr newWin;
  1132.     short nLines;
  1133.     TEHandle teh;
  1134.     Str255 scratch;
  1135.     
  1136.     if (newWin=DoComposeNew(toWhom))
  1137.     {
  1138.         newMessH = (MessType **)newWin->qWindow.refCon;
  1139.         XferCustomTable(origMessH,newMessH);        
  1140.         if (SumOf(origMessH)->flags&FLAG_OUT)
  1141.         {
  1142.             BuildDateHeader(scratch,SumOf(origMessH)->seconds);
  1143.             AppendMessText(newMessH,HEAD_LIMIT-1,scratch+1,*scratch);
  1144.             AppendMessText(newMessH,HEAD_LIMIT-1,"\n",1);
  1145.         }
  1146.         if (win->qWindow.windowKind==COMP_WIN)
  1147.         {
  1148.             short tx;
  1149.             for (tx=0;tx<HEAD_LIMIT;tx++)
  1150.             {
  1151.                 teh = (*origMessH)->txes[tx];
  1152.                 if ((*teh)->teLength)
  1153.                 {
  1154.                     if (tx<HEAD_LIMIT-1)
  1155.                     {
  1156.                         GetRString(scratch,HEADER_STRN+tx+1);
  1157.                         AppendMessText(newMessH,HEAD_LIMIT-1,scratch+1,*scratch);
  1158.                     }
  1159.                     AppendMessText(newMessH,HEAD_LIMIT-1,LDRef((*teh)->hText),(*teh)->teLength);
  1160.                     UL((*teh)->hText);
  1161.                     if ((*(*teh)->hText)[(*teh)->teLength-1]!='\n')
  1162.                         AppendMessText(newMessH,HEAD_LIMIT-1,"\n",1);
  1163.                 }
  1164.                 if (tx==HEAD_LIMIT-2) AppendMessText(newMessH,HEAD_LIMIT-1,"\n",1);
  1165.             }
  1166.             TESetText(LDRef((*(*origMessH)->txes[SUBJ_HEAD-1])->hText),
  1167.                 (*(*origMessH)->txes[SUBJ_HEAD-1])->teLength,
  1168.                 (*newMessH)->txes[SUBJ_HEAD-1]);
  1169.             UL((*(*origMessH)->txes[SUBJ_HEAD-1])->hText);
  1170.         }
  1171.         else
  1172.         {
  1173.             FindAndCopyHeader(origMessH,newMessH,HeaderName(SUBJ_HEAD),SUBJ_HEAD);
  1174.             /*
  1175.              * stick it in
  1176.              */
  1177.             AppendMessText(newMessH,HEAD_LIMIT-1,LDRef((*Win2Body(win))->hText),
  1178.                                                             (*Win2Body(win))->teLength);
  1179.             HUnlock((*Win2Body(win))->hText);
  1180.         }
  1181.         
  1182.         UpdateMyWindow(newWin);
  1183.             
  1184.         /*
  1185.          * make sure the last line is blank
  1186.          */
  1187.         teh = (*newMessH)->txes[HEAD_LIMIT-1];
  1188.         nLines = (*teh)->nLines;
  1189.         if ((*teh)->lineStarts[nLines-1]-(*teh)->lineStarts[nLines-2]==1)
  1190.             nLines--;
  1191.         else
  1192.             AppendMessText(newMessH,HEAD_LIMIT-1,"\n",1);
  1193.  
  1194.         /*
  1195.             * quote them
  1196.             */
  1197.         QuoteLines(newMessH,HEAD_LIMIT-1,0,nLines,FWD_QUOTE);
  1198.         if (win->qWindow.windowKind!=COMP_WIN)
  1199.             SetState((*origMessH)->tocH,(*origMessH)->sumNum,FORWARDED);
  1200.       
  1201.         /*
  1202.          * Attributions
  1203.          */
  1204.         Attribute(FWD_INTRO,origMessH,newMessH,False);
  1205.         Attribute(FWD_TRAIL,origMessH,newMessH,True);
  1206.  
  1207.         /*
  1208.          * copy priority (not my idea)
  1209.          */
  1210.         if (!PrefIsSet(PREF_NO_XF_PRIOR))
  1211.             SumOf(newMessH)->origPriority = SumOf(newMessH)->priority =
  1212.                 SumOf(origMessH)->origPriority;
  1213.         
  1214.         UpdateSum(newMessH,SumOf(newMessH)->offset,SumOf(newMessH)->length);
  1215.         ShowMyWindow(newWin);
  1216.         newWin->isDirty = False;
  1217.     }
  1218.     return(!newWin);
  1219. }
  1220.  
  1221. /************************************************************************
  1222.  * FindAndCopyHeader - pick the given header out of a message, and
  1223.  * copy it to a composition message
  1224.  ************************************************************************/
  1225. int FindAndCopyHeader(MessType **origMH,MessType **newMH,UPtr fromHead,short toHead)
  1226. {
  1227.     UPtr body;
  1228.     long size;
  1229.     int result;
  1230.     
  1231.     body = LDRef((*BodyOf(origMH))->hText);
  1232.     size = SumOf(origMH)->bodyOffset-(*origMH)->weeded;
  1233.     result = TextFindAndCopyHeader(body,size,newMH,fromHead,toHead);
  1234.     HUnlock((*BodyOf(origMH))->hText);
  1235.     return(result);
  1236. }
  1237.  
  1238. /************************************************************************
  1239.  * TextFindAndCopyHeader - pick the given header out of a text block, and
  1240.  * copy it to a composition message
  1241.  ************************************************************************/
  1242. int TextFindAndCopyHeader(UPtr body,long size,MessType **newMH,UPtr fromHead,short toHead)
  1243. {
  1244.     int oldNl;
  1245.     short oldBody;
  1246.     MyWindowPtr newWin = (*newMH)->win;
  1247.     UPtr bodyEnd = body+size;
  1248.     UPtr spot;
  1249.         
  1250.     if (body = FindHeaderString(body,fromHead,&size))
  1251.     {
  1252.         TEHandle teh = (*newMH)->txes[toHead-1];
  1253.         oldNl = CountTeLines(teh);
  1254.         
  1255.         for(;;)
  1256.         {
  1257.             oldBody = (*teh)->teLength;
  1258.             NoScrollTESetSelect(oldBody,oldBody,teh);
  1259.             if (oldBody)
  1260.             {
  1261.                 for (spot=*(*teh)->hText+oldBody-1;spot>*(*teh)->hText;spot--)
  1262.                     if (!IsWhite(*spot))
  1263.                     {
  1264.                         if (*spot!=',')
  1265.                             TEInsert(", ",2,teh);
  1266.                         else if (spot==*(*teh)->hText+oldBody-1)
  1267.                             TEInsert(" ",1,teh);
  1268.                         break;
  1269.                     }
  1270.             }
  1271.             TEInsert(body,size,teh);
  1272.             
  1273.             body += size;
  1274.             while (IsWhite(*body)&&body<bodyEnd) body++;            /* skip to newline */
  1275.             body++;                                                                                     /* skip newline */
  1276.             if (body<bodyEnd && IsWhite(*body))                             /* continuation */
  1277.             {
  1278.                 while (IsWhite(*body)&&body<bodyEnd) body++;
  1279.                 for (spot=body;spot<bodyEnd&&*spot!='\n';spot++);
  1280.                 do {spot--;} while (IsWhite(*spot));
  1281.                 size = spot-body+1;
  1282.                 if (!size) break;
  1283.             }
  1284.             else
  1285.                 break;
  1286.         }
  1287.         
  1288.         INVAL_RECT(&(*teh)->destRect);
  1289.         FixMessTE(newMH,toHead-1,oldNl);
  1290.     }
  1291.     return(body!=nil);
  1292. }
  1293.  
  1294. /************************************************************************
  1295.  * XferCustomTable - transfer a custom table from a message to its reply
  1296.  * (or forward or redirect)
  1297.  ************************************************************************/
  1298. void XferCustomTable(MessHandle origMessH,MessHandle newMessH)
  1299. {
  1300.     short origTable;
  1301.     Boolean isOut;
  1302.     
  1303.     /*
  1304.      * under the old regime, we don't do this step
  1305.      */
  1306.     if (!NewTables) return;
  1307.     
  1308.     origTable = SumOf(origMessH)->tableId;
  1309.  
  1310.     /*
  1311.      * if the original message had the default table, give the default table
  1312.      * to the new message (which has already been done, so return)
  1313.      */
  1314.     if (origTable == DEFAULT_TABLE) return;
  1315.     
  1316.     isOut = (SumOf(origMessH)->flags&FLAG_OUT)!=0;
  1317.     if (origTable == GetRLong(PREF_STRN+(isOut?PREF_OUT_XLATE:PREF_IN_XLATE)))
  1318.         return;
  1319.     
  1320.     /*
  1321.      * if original was outgoing, use same table
  1322.      * also use same table if that table is no table
  1323.      */
  1324.     if (isOut || !origTable)
  1325.         SumOf(newMessH)->tableId = origTable;
  1326.         
  1327.     /*
  1328.      * Otherwise, look for an out table that corresponds to the In table
  1329.      * and use that.
  1330.      */
  1331.     else if (GetResource('taBL',origTable+1))
  1332.         SumOf(newMessH)->tableId = origTable+1;
  1333. }
  1334.